/* 
Copyright (c) 2004,2005, 2006, 2007, 2008 University of Illinois 

All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.

3. The name of the author may not be used to endorse or promote products
   derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/** @fileoverview
 * Handles keyboard enhancements dialog generation and control, also provides a class which encapsulates keyboard enhancements 
 * accessor functionality.
 */

// FIXME: This should be setup as a generic KeyboardEnhancements class that gets extended to KeyboardEnhancementsDialog with the GUI/dialog functions.

// Constructor
// The argument is the preference name, like "accessext.keyboard.enhancements.feature.prev.header". 
// This form is important because other id/preference/strings are generated from it.
AccessibilityExtension.ns(function() {  with (AccessibilityExtension.LIB) {
	var AE = AccessibilityExtension.ns;  
	
	function KeyboardEnhancement(name, type) {
		this.name = name;
		this.type = type;
	
		this.id = name.replace(/\./g,"-");
	
		this.defaultKey = accessext_getBundleSingleString(name + ".default");
	
		// Get key description
		this.desc = accessext_getBundleSingleString(name + ".description");
		
		this.initOverrideKeys();
	}

	// Encapsulates data relating to keyboard enhancements and the various ways to refer to their different attributes.
	// Provides functionality relating to loading, saving, validating, and presenting the bindings.
	KeyboardEnhancement.prototype = {
		name : null,
		id : null,
		desc : null,
		defaultKey : null,
		type : "key", // other type is "override", for convienience in methods which check accepted keys, below
		acceptKeyUser : [
			"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z",
			"1","2","3","4","5","6","7","8","9","0"	],
		acceptKeyDOM : [
			"DOM_VK_A","DOM_VK_B","DOM_VK_C","DOM_VK_D","DOM_VK_E","DOM_VK_F","DOM_VK_G","DOM_VK_H","DOM_VK_I","DOM_VK_J","DOM_VK_K","DOM_VK_L","DOM_VK_M","DOM_VK_N","DOM_VK_O","DOM_VK_P","DOM_VK_Q","DOM_VK_R","DOM_VK_S","DOM_VK_T","DOM_VK_U","DOM_VK_V","DOM_VK_W","DOM_VK_X","DOM_VK_Y","DOM_VK_Z",
			"DOM_VK_1","DOM_VK_2","DOM_VK_3","DOM_VK_4","DOM_VK_5","DOM_VK_6","DOM_VK_7","DOM_VK_8","DOM_VK_9","DOM_VK_0" ],
		acceptOverrideUser : null,
		acceptOverrideDOM : [
			"DOM_VK_F2","DOM_VK_F3","DOM_VK_F4","DOM_VK_F5","DOM_VK_F6","DOM_VK_F8","DOM_VK_F9","DOM_VK_F10",
			"DOM_VK_ENTER",
			"DOM_VK_INSERT",		
			"DOM_VK_DELETE",
			"DOM_VK_PRINTSCREEN",
			"DOM_VK_PAUSE",
			"DOM_VK_SCROLL_LOCK" ],
	
		// Needed because we need to get the string bundle, and that can't really happen when the extension is first loaded
		initOverrideKeys : function() {
			this.acceptOverrideUser = [
				"F2","F3","F4","F5","F6","F8","F9","F10",
				accessext_getBundleSingleString("accessext.keyboard.enhancements.find.as.you.type.override.key.enter"),
				accessext_getBundleSingleString("accessext.keyboard.enhancements.find.as.you.type.override.key.insert"),		
				accessext_getBundleSingleString("accessext.keyboard.enhancements.find.as.you.type.override.key.delete"),
				accessext_getBundleSingleString("accessext.keyboard.enhancements.find.as.you.type.override.key.printscreen"),
				accessext_getBundleSingleString("accessext.keyboard.enhancements.find.as.you.type.override.key.pause"),		
				accessext_getBundleSingleString("accessext.keyboard.enhancements.find.as.you.type.override.key.scrolllock")	
			];
		},
		// returns true if the given key is an acceptable value for a keyboard enhancement preference of this.type
		isAccepted : function(key) {
			if(this.isOverride()) {
				return this.isAcceptedOverride(key);
			} 
			return this.isAcceptedKey(key);
		},
		// returns true if the given key is an acceptable value for a keyboard enhancement key
		isAcceptedKey : function(key) {
			if(this.indexAcceptedKey(key) == -1) {
				return false;
			}
			return true;
		},
		// returns true if the given key is an acceptable value for a keyboard enhancement find-as-you-type override
		isAcceptedOverride : function(key) {
			if(this.indexAcceptedOverride(key) == -1) {
				return false;
			}
			return true;
		},
		// returns the index if the given key is an acceptable value or -1 if not found/unacceptable
		indexAcceptedKey : function(key) {
			for(var i=0; i<this.acceptKeyUser.length; i++) {
				if( this.acceptKeyUser[i] == key.toUpperCase() ) {
					return(i);
				}
			}
			return(-1);
		},
		// returns the index if the given key is an acceptable value or -1 if not found/unacceptable	
		indexAcceptedOverride : function(key) {
			for(var i=0; i<this.acceptOverrideUser.length; i++) {
				if( this.acceptOverrideUser[i] == key.toUpperCase() ) {
					return(i);
				}
			}
			return(-1);
		},
		/** Is this key enhancement of the "override" type?
		 * @returns True if this keyboard enhancement is of type "override"
		 * @type boolean
		 */
		isOverride : function() {
			return this.type.toUpperCase() == "OVERRIDE";
		},
		// returns the DOM constant string that refers to the key, or the empty string if not found/unacceptable
		getDOMString : function(key) {
			var idx = this.indexAcceptedKey(key);
			if( idx != -1 ) {
				return this.acceptKeyDOM[idx];
			}
	
			// If wasn't in the "key" array, try the "override" array
			idx = this.indexAcceptedOverride(key);
			if( idx != -1 ) {
				return this.acceptOverrideDOM[idx];
			}
			
			return ""; // Not found
		},
		// inverse of getDOMString()
		getUserString : function(domKey) {
			// Join the arrays together so we need only search over one of them
			var user = this.acceptKeyUser.concat(this.acceptOverrideUser);
			var dom = this.acceptKeyDOM.concat(this.acceptOverrideDOM);
			for(var i=0; i<dom.length; i++) {
				if( dom[i] == domKey ) {
					return user[i];
				}
			}
		},
		// Writes the current state of the element in the DOM to the keyboard enhancement preferences 
		// Returns true on success, returns false if the user's key is not "acceptable"
		saveKeyPrefFromDOM : function() {
			var pref = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService).getBranch("");
			var newKey = this.getKeyFromDOM();
			if( this.isAccepted(newKey) ) {
				pref.setCharPref(this.name, newKey);
				return true;
			}
			return false;
		},
		getKeyFromDOM : function() {
			var e = document.getElementById(this.id);
			if(this.isOverride()) {
				return e.getAttribute("label").toUpperCase();
			}
			return e.value.toUpperCase();
		},
		// Returns the preferred key (or default) for this enhancement
		getKey : function() {
			var pref = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService).getBranch("");
		
			if( pref.prefHasUserValue(this.name) ) {
				return( pref.getCharPref(this.name) );
			}
			return( this.defaultKey );
		}
	},

AccessibilityExtension.LIB.extend(AccessibilityExtension.LIB,
{
	newKeyboardEnhancement: function(name, type) { 
		var n = new KeyboardEnhancement(name, type);
		return n; 
	},
	
	// returns an array of keyboard enhancement objects
	getAllEnhancements: function() {
		var enhancement = new Array();
		var enhancementPrefName = [
			"accessext.keyboard.enhancements.feature.prev.header",
			"accessext.keyboard.enhancements.feature.next.header",
			"accessext.keyboard.enhancements.feature.prev.landmark",
			"accessext.keyboard.enhancements.feature.next.landmark",
			"accessext.keyboard.enhancements.feature.prev.link",
			"accessext.keyboard.enhancements.feature.next.link",
			"accessext.keyboard.enhancements.feature.prev.navlist",
			"accessext.keyboard.enhancements.feature.next.navlist"				
		];
	
		enhancement.push(new KeyboardEnhancement("accessext.keyboard.enhancements.form.field.override.key","override"));	
		for(var i=0; i<enhancementPrefName.length; i++) {
			enhancement.push(new KeyboardEnhancement(enhancementPrefName[i],"key"));
		}
		return enhancement;
	},
	
	// Loads enhancements and constructs the GUI dialog
	generateKeyboardEnhancementsList: function() {
		var listbox = document.getElementById("listbox");
		if (listbox == null) return; //not in keyboardenhancements.xul so skip this function
		
		var enhancement = getAllEnhancements();
	
		for(var i=0; i<enhancement.length; i++) {
			if(enhancement[i].isOverride()) {
				addOverride(enhancement[i]);
			} else {
				addListboxNode(enhancement[i]);
			}
		}
	},
	
	accessextKeyboardEnhancementsReset: function() {
		var enhancement = getAllEnhancements();
	
		for(var i=0; i<enhancement.length; i++) {
			if(enhancement[i].isOverride()) {
				continue;
			}
			// must use the property .value and not the attribute "value" (the attribute is only for initialization)
			document.getElementById(enhancement[i].id).value = enhancement[i].defaultKey;
		}
		// Set the override key, specially
		document.getElementById(enhancement[0].id).setAttribute("label",enhancement[0].defaultKey);
	},
	
	accessextKeyboardEnhancementsSave: function() {
		var enhancement = getAllEnhancements();
	
		// Verify input
		for(var i=0; i<enhancement.length; i++) {
			if( !enhancement[i].isAccepted(enhancement[i].getKeyFromDOM()) ) {
				alert(
					accessext_getBundleSingleString("accessext.keyboard.enhancements.key.error")
					+ "\n\n     " + enhancement[i].desc
				);
				return false;
			}
		}
		
		// Save all inputsVerify input
		for(var i=0; i<enhancement.length; i++) {
			enhancement[i].saveKeyPrefFromDOM();
		}
		return true;
	},
	
	// Adds the combobox/menulist for the acceptable override keys
	// FIXME: Specific to the form-field-override menulist
	addOverride: function(override) {
		var menupopup = document.createElement("menupopup");
		
		var menuitem;
		for(var i=0; i<override.acceptOverrideUser.length; i++) {
			menuitem = document.createElement("menuitem");
			menuitem.setAttribute("label",override.acceptOverrideUser[i]);
			menupopup.appendChild(menuitem);
		}
		
		var menulist = document.getElementById("accessext-keyboard-enhancements-form-field-override-key");
		menulist.appendChild(menupopup);
		menulist.setAttribute("label",override.getKey());	
	},
	
	// Adds a row to the dialog's listbox representing the enhancement
	addListboxNode: function(enhancement) {
	
		/* Shooting for something like this:
		<xul:listitem allowevents="true">
			<xul:listcell label="foo"/>
			<xul:listcell><xul:textbox maxlength="1" size="1"/></xul:listcell>
		</xul:listitem>	
		*/
		
		var listitem = document.createElement("listitem");
		listitem.setAttribute("allowevents","true");
		
		var feature = document.createElement("listcell");
		feature.setAttribute("label",enhancement.desc);
		
		var key = document.createElement("textbox");
		
		key.setAttribute("size","1");
		key.setAttribute("maxlength","1");
		key.setAttribute("id",enhancement.id);
		
		key.setAttribute("value",enhancement.getKey());
	
		var listcell;
		listcell = document.createElement("listcell");
		listcell.appendChild(key);
		key = listcell;
	
		listitem.appendChild(feature);
		listitem.appendChild(key);	
		
		var bindingsRows = document.getElementById("listbox");
		bindingsRows.appendChild(listitem);
	}
});
AccessibilityExtension.LIB.generateKeyboardEnhancementsList();	
}});